package com.google.code.p.restrpc.http;

import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.apache.commons.lang.StringUtils;

public class HttpHeaders {
    
    private static final SimpleDateFormat[] DATE_FORMATS = new SimpleDateFormat[] { new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz",
                                                                                                         Locale.US),
                                                                                   new SimpleDateFormat("EEE, dd-MMM-yy HH:mm:ss zzz",
                                                                                                        Locale.US),
                                                                                   new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy",
                                                                                                        Locale.US) };
    
    static {
        TimeZone GMT = TimeZone.getTimeZone("GMT");
        for (SimpleDateFormat simpleDateFormat : DATE_FORMATS) {
            simpleDateFormat.setTimeZone(GMT);
        }
    }
    
    protected final Map<String, List<String>> headers = new HashMap<String, List<String>>(8);
    
    public HttpHeaders() {
    }
    
    public List<String> getValues(String headerName) {
        return headers.get(headerName);
    }
    
    public String getFirst(String headerName) {
        List<String> headerValues = headers.get(headerName);
        return headerValues != null ? ((LinkedList<String>) headerValues).getFirst()
                                   : null;
    }
    
    public void addHeader(String headerName, String... headerValues) {
        List<String> values = headers.get(headerName);
        if (values == null) {
            values = new LinkedList<String>();
            this.headers.put(headerName, values);
        }
        values.addAll(Arrays.asList(headerValues));
    }
    
    public void setHeader(String headerName, String... headerValues) {
        List<String> values = new LinkedList<String>();
        values.addAll(Arrays.asList(headerValues));
        headers.put(headerName, values);
    }
    
    public long getFirstDate(String headerName) {
        String headerValue = getFirst(headerName);
        if (headerValue == null) {
            return -1;
        }
        for (SimpleDateFormat simpleDateFormat : DATE_FORMATS) {
            try {
                return simpleDateFormat.parse(headerValue).getTime();
            }
            catch (ParseException e) {
                // ignore
            }
        }
        throw new IllegalArgumentException("Cannot parse date value \"" + headerValue
                                           + "\" for \""
                                           + headerName
                                           + "\" header");
    }
    
    public void setDate(String headerName, long date) {
        setHeader(headerName, DATE_FORMATS[0].format(new Date(date)));
    }
    
    // *************************************************
    public void setAccept(String... acceptable) {
        setHeader(org.apache.http.HttpHeaders.ACCEPT, acceptable);
    }
    
    public List<String> getAccepts() {
        return getValues(org.apache.http.HttpHeaders.ACCEPT);
    }
    
    public void setAcceptCharset(String... acceptableCharsets) {
        setHeader(org.apache.http.HttpHeaders.ACCEPT_CHARSET,
                  acceptableCharsets);
    }
    
    public List<String> getAcceptCharset() {
        return getValues(org.apache.http.HttpHeaders.ACCEPT_CHARSET);
    }
    
    public void setAllows(HttpMethod... allowedMethods) {
        List<String> values = new LinkedList<String>();
        for (HttpMethod method : allowedMethods) {
            values.add(method.value());
        }
        headers.put(org.apache.http.HttpHeaders.ALLOW, values);
    }
    
    public List<String> getAllows() {
        return getValues(org.apache.http.HttpHeaders.ALLOW);
    }
    
    public void setCacheControl(String cacheControl) {
        setHeader(org.apache.http.HttpHeaders.CACHE_CONTROL, cacheControl);
    }
    
    public String getCacheControl() {
        return getFirst(org.apache.http.HttpHeaders.CACHE_CONTROL);
    }
    
    public void setContentLength(long contentLength) {
        setHeader(org.apache.http.HttpHeaders.CONTENT_LENGTH,
                  Long.toString(contentLength));
    }
    
    public long getContentLength() {
        String value = getFirst(org.apache.http.HttpHeaders.CONTENT_LENGTH);
        return (value != null ? Long.parseLong(value) : -1);
    }
    
    public void setContentType(String... contentType) {
        setHeader(org.apache.http.HttpHeaders.CONTENT_TYPE, contentType);
    }
    
    public String getContentType() {
        return getFirst(org.apache.http.HttpHeaders.CONTENT_TYPE);
    }
    
    public void setConnectionClose(boolean close) {
        if (close) {
            setHeader(org.apache.http.HttpHeaders.CONNECTION, "close");
        }
    }
    
    public boolean getConnectionClose() {
        String connectonClose = getFirst(org.apache.http.HttpHeaders.CONNECTION);
        return !(StringUtils.isEmpty(connectonClose) || !"close".equals(connectonClose));
    }
    
    public void setUserAgent(String... userAgent) {
        setHeader(org.apache.http.HttpHeaders.USER_AGENT, userAgent);
    }
    
    public String getUserAgent() {
        return getFirst(org.apache.http.HttpHeaders.USER_AGENT);
    }
    
    public List<String> getContentTypes() {
        return getValues(org.apache.http.HttpHeaders.CONTENT_TYPE);
    }
    
    public void setDate(long date) {
        setDate(org.apache.http.HttpHeaders.DATE, date);
    }
    
    public long getDate() {
        return getFirstDate(org.apache.http.HttpHeaders.DATE);
    }
    
    public void setETag(String eTag) {
        if (eTag != null) {
            if (eTag.startsWith("\"") || eTag.startsWith("W/")) {
                throw new IllegalArgumentException("Invalid eTag, does not start with W/ or \"");
            }
            if (eTag.endsWith("\"")) {
                throw new IllegalArgumentException("Invalid eTag, does not end with \"");
            }
        }
        setHeader(org.apache.http.HttpHeaders.ETAG, eTag);
    }
    
    public String getETag() {
        return getFirst(org.apache.http.HttpHeaders.ETAG);
    }
    
    public void setExpires(long expires) {
        setDate(org.apache.http.HttpHeaders.EXPIRES, expires);
    }
    
    public long getExpires() {
        return getFirstDate(org.apache.http.HttpHeaders.EXPIRES);
    }
    
    public void setIfModifiedSince(long ifModifiedSince) {
        setDate(org.apache.http.HttpHeaders.IF_MODIFIED_SINCE, ifModifiedSince);
    }
    
    public long getIfNotModifiedSince() {
        return getFirstDate(org.apache.http.HttpHeaders.IF_MODIFIED_SINCE);
    }
    
    public void setIfNoneMatch(String ifNoneMatch) {
        setHeader(org.apache.http.HttpHeaders.IF_NONE_MATCH, ifNoneMatch);
    }
    
    public void setLastModified(long lastModified) {
        setDate(org.apache.http.HttpHeaders.LAST_MODIFIED, lastModified);
    }
    
    public long getLastModified() {
        return getFirstDate(org.apache.http.HttpHeaders.LAST_MODIFIED);
    }
    
    public void setLocation(URI location) {
        setHeader(org.apache.http.HttpHeaders.LOCATION,
                  location.toASCIIString());
    }
    
    public URI getLocation() {
        String value = getFirst(org.apache.http.HttpHeaders.LOCATION);
        return (value != null ? URI.create(value) : null);
    }
    
    public void setPragma(String pragma) {
        setHeader(org.apache.http.HttpHeaders.PRAGMA, pragma);
    }
    
    public String getPragma() {
        return getFirst(org.apache.http.HttpHeaders.PRAGMA);
    }
    
}
